package com.zt.bigdata.vertx.interceptor

import com.ecfront.dew.common.Resp
import com.typesafe.scalalogging.LazyLogging
import io.vertx.ext.web.RoutingContext

import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.{Future, Promise}
import scala.reflect.ClassTag

/**
  *
  */
class FilterChain[E <: Animal, R <: Object](processFunction: ProcessFunction[E, R],
                                            seq: Seq[HttpAsyncFilter[E, R]])(implicit r: ClassTag[R]) extends LazyLogging {

  def process(routingContext: RoutingContext, e: E): Future[Resp[R]] = {

    val future = Promise[Resp[R]]()

    val beforeP = Promise[Resp[R]]()
    doProcess(routingContext, e, seq, true, beforeP)
    beforeP.future.onSuccess {
      case beforeResp => if (beforeResp.ok) {
        if (null != processFunction) {
          processFunction.process(e, routingContext).onSuccess {
            case execResp => if (execResp.ok) {
              val afterP = Promise[Resp[R]]()
              doProcess(routingContext, e, seq.reverse, false, afterP);
              afterP.future.onSuccess {
                case afterResp => if (afterResp.ok()) {
                  future.success(afterResp)
                } else {
                  //后置 filter 失败 直接返回
                  future.success(afterResp)
                }
              }
            } else {
              logger.warn(s"Execute interceptor error : ${execResp.getBody}")
              future.success(execResp.asInstanceOf[Resp[R]])
            }
          }
        } else {
          val afterP = Promise[Resp[R]]()
          doProcess(routingContext, e, seq.reverse, false, afterP);
          afterP.future.onSuccess {
            case afterResp => if (afterResp.ok) {
              future.success(afterResp)
            } else {
              //后置 filter 失败 直接返回
              future.success(afterResp)
            }
          }
        }
      } else {
        //前置filter 失败 直接返回
        logger.warn(s"Execute before interceptor error : ${beforeResp.getBody}")
        future.success(beforeResp)
      }
    }
    future.future
  }

  def doProcess(routingContext: RoutingContext,
                e: E,
                seq: Seq[HttpAsyncFilter[E, R]],
                isBefore: Boolean,
                future: Promise[Resp[R]]): Unit = {

    if (seq.nonEmpty) {
      val execFuture = if (isBefore) {
        seq.head.before(routingContext, e)
      } else {
        seq.head.after(routingContext, e)
      }

      execFuture.onSuccess {
        case resp => if (resp.ok()) {
          if (seq.tail.nonEmpty) {
            doProcess(routingContext, e, seq.tail, isBefore, future)
          } else {
            future.success(resp)
          }
        } else {
          future.success(resp)
        }
      }
    } else {
      future.success(Resp.success(r.runtimeClass.asInstanceOf[R]))
    }

  }
}